using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Web.Services.Description;
using System.CodeDom;
using System.Web.Services.Discovery;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

namespace AJAXServiceProxyGeneration
{
    /// <summary>
    /// Generates client code definition for Web Services
    /// </summary>
    public abstract class ServiceGenerator : IGenerator
    {
        private string _serviceNamespace;

        public string ServiceNamespace
        {
            get { return _serviceNamespace; }
        }

        private string _pocoNamespace;

        public string POCONamespace
        {
            get { return this._pocoNamespace; }
        }

        private string _wsdlUri;

        public string WsdlUri
        {
            get { return _wsdlUri; }
        }


        public ServiceGenerator(string serviceNamespace,string pocoNamespace, string wsdlUri)
        {
            this._serviceNamespace = serviceNamespace;
            this._pocoNamespace = pocoNamespace;
            this._wsdlUri = wsdlUri;
        }
        /// <summary>
        /// Generate the code
        /// </summary>
        /// <param name="serviceNamespace">Namespace of the WebService</param>
        /// <param name="wsdlUriString">A valid Uri to the WSDL Document</param>
        /// <returns></returns>
        public virtual void Generate()
        {
            try
            {
                string[] lines = this.WsdlUri.Split(new string[] {Environment.NewLine, "\n","\r"},StringSplitOptions.RemoveEmptyEntries);
                foreach (string line in lines)
                {
                    //Fetch the WSDL
                    HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(line);
                    webRequest.Method = "Get";

                    using (WebResponse webResponse = webRequest.GetResponse())
                    {
                        using (Stream responseStream = webResponse.GetResponseStream())
                        {
                            //Expecting a WSDL
                            using (XmlTextReader responseTextReader = new XmlTextReader(responseStream))
                            {
                                //Read WSDL from URL
                                ServiceDescription sd = ServiceDescription.Read(responseTextReader);
                                sd.RetrievalUrl = line;
                                this.GenerateCode(this.ServiceNamespace, sd);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Unable to Generate Client",ex);
            }
        }
        protected virtual void GenerateCode(string serviceNamespace,ServiceDescription serviceDescription)
        {
            try
            {
                CodeNamespace codeNamespace = new CodeNamespace(serviceNamespace);

                CodeCompileUnit unit = new CodeCompileUnit();
                unit.Namespaces.Add(codeNamespace);

                DiscoveryClientProtocol discoveryClientProtocol = new DiscoveryClientProtocol();
                discoveryClientProtocol.DiscoverAny(serviceDescription.RetrievalUrl);

                WebReferenceCollection wref = new WebReferenceCollection();

                //Create a WebReference
                WebReference webReference = new WebReference(
                    discoveryClientProtocol.Documents,
                    codeNamespace
                    );
                wref.Add(webReference);

                WebReferenceOptions webReferenceOptions = new WebReferenceOptions();
                webReferenceOptions.Style = ServiceDescriptionImportStyle.Client;
                webReferenceOptions.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.None;
                webReferenceOptions.Verbose = true;

                CodeDomProvider provider = new CSharpCodeProvider();
                ServiceDescriptionImporter.GenerateWebReferences(wref, provider, unit, webReferenceOptions);

                StringBuilder sb = new StringBuilder();
                using (StringWriter sw = new StringWriter(sb))
                {
                    CodeDomProvider codeProvider = new CSharpCodeProvider();
                    //Manipulate the CodeDom
                    this.ManipulateCodeDom(codeNamespace);
                    ////Setup Compile Parameters
                    codeProvider.GenerateCodeFromNamespace(codeNamespace, sw, null);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        protected abstract void ManipulateCodeDom(CodeNamespace codeNamespace);

        #region IGenerator Members


        private IGeneratorWriter _generatorWriter;


        public IGeneratorWriter ServiceClassGeneratorWriter
        {
            get { return this._generatorWriter; }
            set { this._generatorWriter = value; }
        }

        private IGeneratorWriter _generatorPOCOWriter;

        /// <summary>
        /// Writer for the POCO classes
        /// </summary>
        public IGeneratorWriter POCOClassGeneratorWriter
        {
            get
            {
                if (this._generatorPOCOWriter == null)
                {
                    throw new NullReferenceException("GeneratorWriter");
                }
                return this._generatorPOCOWriter;
            }
            set { this._generatorPOCOWriter = value; }
        }

        #endregion
    }
}

